namespace Filters.Demo.FilterControls
{
    using System;
    using System.ComponentModel;
    using System.Drawing;
    using System.Windows.Forms;
    using AForge.Imaging.Filters;

    /// <summary>
    /// FilterPreview window
    /// </summary>
    public class FilterPreview : System.Windows.Forms.Control
    {
        private Bitmap previewImage;
        private Bitmap image;
        private IFilter filter;
        private Pen blackPen = new Pen(Color.Black, 1);
        private Cursor cursorHand;
        private Cursor cursorHandMove;
        private int areaWidth;
        private int areaHeight;
        private int imageX;
        private int imageY;
        private int startTrackingX;
        private int startTrackingY;
        private int oldImageX;
        private int oldImageY;
        private bool tracking;

        // Constructor
        public FilterPreview()
        {
            this.InitializeComponent();

            this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.ResizeRedraw | ControlStyles.DoubleBuffer | ControlStyles.UserPaint, true);

            // load cursors
            var assembly = this.GetType().Assembly;
            this.cursorHand = new Cursor(assembly.GetManifestResourceStream("Filters.Demo.Resources.harrow.cur"));
            this.cursorHandMove = new Cursor(assembly.GetManifestResourceStream("Filters.Demo.Resources.hmove.cur"));
        }

        // Image property
        [Browsable(false)]
        public Bitmap Image
        {
            get
            {
                return this.image;
            }

            set
            {
                this.image = value;

                if (value != null)
                {
                    // calculate size of preview area
                    this.areaWidth = Math.Min(this.ClientRectangle.Width - 2, this.image.Width);
                    this.areaHeight = Math.Min(this.ClientRectangle.Height - 2, this.image.Height);

                    // calculate image position
                    this.imageX = (this.image.Width - this.areaWidth) >> 1;
                    this.imageY = (this.image.Height - this.areaHeight) >> 1;
                }

                this.RefreshFilter();
            }
        }

        // Filter property
        [Browsable(false)]
        public AForge.Imaging.Filters.IFilter Filter
        {
            set
            {
                this.filter = value;
                this.RefreshFilter();
            }
        }

        // Refresh preview
        public void RefreshFilter()
        {
            // release old image
            if (this.previewImage != null)
            {
                this.previewImage.Dispose();
                this.previewImage = null;
            }

            if ((this.image != null) && (this.filter != null))
            {
                var tmp = this.image.Clone(new Rectangle(this.imageX, this.imageY, this.areaWidth, this.areaHeight), this.image.PixelFormat);

                try
                {
                    this.previewImage = this.filter.Apply(tmp);
                }
                catch
                {
                }

                // release temp image
                tmp.Dispose();
            }

            // repaint
            this.Invalidate();
        }

        // Dispose
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                this.blackPen.Dispose();

                if (this.previewImage != null)
                {
                    this.previewImage.Dispose();
                    this.cursorHand.Dispose();
                    this.cursorHandMove.Dispose();
                }
            }

            base.Dispose(disposing);
        }

        // Paint control
        protected override void OnPaint(PaintEventArgs pe)
        {
            var g = pe.Graphics;
            var rc = ClientRectangle;
            var width = rc.Width;
            var height = rc.Height;

            // calculate size of preview area
            if (this.image != null)
            {
                width = this.areaWidth + 2;
                height = this.areaHeight + 2;
            }

            // calculate position of preview area
            var x = (rc.Width - width) >> 1;
            var y = (rc.Height - height) >> 1;

            // draw rectangle
            g.DrawRectangle(this.blackPen, x, y, width - 1, height - 1);

            x++;
            y++;

            if (this.image != null)
            {
                if (this.previewImage == null)
                {
                    // draw original image
                    g.DrawImage(this.image, new Rectangle(x, y, this.areaWidth, this.areaHeight), this.imageX, this.imageY, this.areaWidth, this.areaHeight, GraphicsUnit.Pixel);
                }
                else
                {
                    // draw preview image
                    g.DrawImage(this.previewImage, x, y, this.areaWidth, this.areaHeight);
                }
            }

            // Calling the base class OnPaint
            base.OnPaint(pe);
        }

        // Initialize control
        private void InitializeComponent()
        {
            this.MouseUp += this.FilterPreviewMouseUp;
            this.MouseMove += this.FilterPreviewMouseMove;
            this.MouseDown += this.FilterPreviewMouseDown;
        }

        // On mouse move
        private void FilterPreviewMouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            if (this.image != null)
            {
                if (!this.tracking)
                {
                    // calculate position of preview area
                    var x = ((this.ClientRectangle.Width - this.areaWidth - 2) >> 1) + 1;
                    var y = ((this.ClientRectangle.Height - this.areaHeight - 2) >> 1) + 1;

                    // check mouse coordinates
                    if ((e.X >= x) && (e.Y >= y) && (e.X < x + this.areaWidth) && (e.Y < y + this.areaHeight))
                    {
                        this.Cursor = this.cursorHand;
                    }
                    else
                    {
                        this.Cursor = Cursors.Default;
                    }
                }
                else
                {
                    var dx = e.X - this.startTrackingX;
                    var dy = e.Y - this.startTrackingY;

                    this.imageX = Math.Max(0, Math.Min(this.image.Width - this.areaWidth, this.oldImageX - dx));
                    this.imageY = Math.Max(0, Math.Min(this.image.Height - this.areaHeight, this.oldImageY - dy));

                    this.Invalidate();

                    this.Cursor = this.cursorHandMove;
                }
            }
        }

        // On mouse button down
        private void FilterPreviewMouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            if ((this.image != null) && (e.Button == MouseButtons.Left))
            {
                // start tracking
                this.tracking = true;
                this.Capture = true;

                this.startTrackingX = e.X;
                this.startTrackingY = e.Y;

                this.oldImageX = this.imageX;
                this.oldImageY = this.imageY;

                // release preview image
                if (this.previewImage != null)
                {
                    this.previewImage.Dispose();
                    this.previewImage = null;
                }

                // repaint
                this.Invalidate();

                // set cursor
                this.Cursor = this.cursorHandMove;
            }
        }

        // On mouse button up
        private void FilterPreviewMouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            if (this.tracking)
            {
                // stop tracking
                this.tracking = false;
                this.Capture = false;

                this.RefreshFilter();

                // set cursor
                this.Cursor = this.cursorHand;
            }
        }
    }
}
